use std::{iter::Peekable, slice::Iter};

use crate::{
    data::{KeywordType, SymbolType},
    error::{CompilerError, CompilerResult},
    token::Token,
};

/// Statement compiler unit
pub(crate) struct StatementCompilerUnit;

impl StatementCompilerUnit {
    /// The compiler flow unit of statements
    pub(super) fn compile_statements(
        tokens: &mut Peekable<Iter<'_, Token>>,
    ) -> CompilerResult<String> {
        let mut output = vec!["<statements>".to_string()];
        while let Some(token) = tokens.peek()
            && **token != Token::Symbol(SymbolType::CloseBrace)
        {
            output.push(Self::compile_statement(tokens)?);
        }
        output.push("</statements>".into());

        Ok(output.join("\n"))
    }
    /// The compiler flow unit of statement
    fn compile_statement(tokens: &mut Peekable<Iter<'_, Token>>) -> CompilerResult<String> {
        let token = tokens
            .peek()
            .ok_or_else(|| CompilerError::Statement("not token".into()))?;
        match token {
            Token::Keyword(KeywordType::Let) => Self::compile_let_statement(tokens),
            Token::Keyword(KeywordType::If) => Self::compile_if_statement(tokens),
            Token::Keyword(KeywordType::While) => Self::compile_while_statement(tokens),
            Token::Keyword(KeywordType::Do) => Self::compile_do_statement(tokens),
            Token::Keyword(KeywordType::Return) => Self::compile_return_statement(tokens),
            _ => Err(CompilerError::Statement(
                "not a let/if/while/do/return statement".into(),
            )),
        }
    }
    /// The compiler flow unit of return statement
    fn compile_return_statement(tokens: &mut Peekable<Iter<'_, Token>>) -> CompilerResult<String> {
        let mut output = vec!["<returnStatement>".to_string()];
        // return
        output.push(super::Analyzer::match_keyword(
            tokens
                .next()
                .ok_or_else(|| CompilerError::Statement("must start with return".into()))?,
            KeywordType::Return,
        )?);
        if let Some(Token::Symbol(SymbolType::Semicolon)) = tokens.peek() {
            let _ = tokens.next();
            output.push(SymbolType::Semicolon.to_xml());
        } else {
            // expression
            output.push(super::expression::ExpressionCompilerUnit::compile_expression(tokens)?);
            // ;
            output.push(super::Analyzer::match_symbol(
                tokens
                    .next()
                    .ok_or_else(|| CompilerError::Statement("miss ;".into()))?,
                SymbolType::Semicolon,
            )?);
        }
        output.push("</returnStatement>".into());
        Ok(output.join("\n"))
    }

    /// The compiler flow unit of do statement
    fn compile_do_statement(tokens: &mut Peekable<Iter<'_, Token>>) -> CompilerResult<String> {
        let mut output = vec!["<doStatement>".to_string()];
        // do
        output.push(super::Analyzer::match_keyword(
            tokens
                .next()
                .ok_or_else(|| CompilerError::Statement("must start with do".into()))?,
            KeywordType::Do,
        )?);
        // subroutineCall
        output.push(
            super::expression::ExpressionCompilerUnit::compile_subroutine_call(tokens, None)?,
        );
        // ;
        output.push(super::Analyzer::match_symbol(
            tokens
                .next()
                .ok_or_else(|| CompilerError::Statement("miss ;".into()))?,
            SymbolType::Semicolon,
        )?);
        output.push("</doStatement>".into());
        Ok(output.join("\n"))
    }

    /// The compiler flow unit of while statement
    fn compile_while_statement(tokens: &mut Peekable<Iter<'_, Token>>) -> CompilerResult<String> {
        let mut output = vec!["<whileStatement>".to_string()];
        // while
        output.push(super::Analyzer::match_keyword(
            tokens
                .next()
                .ok_or_else(|| CompilerError::Statement("must start with while".into()))?,
            KeywordType::While,
        )?);
        // (
        output.push(super::Analyzer::match_symbol(
            tokens
                .next()
                .ok_or_else(|| CompilerError::Statement("miss (".into()))?,
            SymbolType::OpenParenthesis,
        )?);
        // expression
        output.push(super::expression::ExpressionCompilerUnit::compile_expression(tokens)?);
        // )
        output.push(super::Analyzer::match_symbol(
            tokens
                .next()
                .ok_or_else(|| CompilerError::Statement("miss )".into()))?,
            SymbolType::CloseParenthesis,
        )?);
        // {
        output.push(super::Analyzer::match_symbol(
            tokens
                .next()
                .ok_or_else(|| CompilerError::Statement("miss {".into()))?,
            SymbolType::OpenBrace,
        )?);
        // statements
        output.push(Self::compile_statements(tokens)?);
        // }
        output.push(super::Analyzer::match_symbol(
            tokens
                .next()
                .ok_or_else(|| CompilerError::Statement("miss }".into()))?,
            SymbolType::CloseBrace,
        )?);
        output.push("</whileStatement>".into());
        Ok(output.join("\n"))
    }

    /// The compiler flow unit of if statement
    fn compile_if_statement(tokens: &mut Peekable<Iter<'_, Token>>) -> CompilerResult<String> {
        let mut output = vec!["<ifStatement>".to_string()];
        // if
        output.push(super::Analyzer::match_keyword(
            tokens
                .next()
                .ok_or_else(|| CompilerError::Statement("must start with if".into()))?,
            KeywordType::If,
        )?);
        // (
        output.push(super::Analyzer::match_symbol(
            tokens
                .next()
                .ok_or_else(|| CompilerError::Statement("miss (".into()))?,
            SymbolType::OpenParenthesis,
        )?);
        // expression
        output.push(super::expression::ExpressionCompilerUnit::compile_expression(tokens)?);
        // )
        output.push(super::Analyzer::match_symbol(
            tokens
                .next()
                .ok_or_else(|| CompilerError::Statement("miss )".into()))?,
            SymbolType::CloseParenthesis,
        )?);
        // {
        output.push(super::Analyzer::match_symbol(
            tokens
                .next()
                .ok_or_else(|| CompilerError::Statement("miss {".into()))?,
            SymbolType::OpenBrace,
        )?);
        // statements
        output.push(Self::compile_statements(tokens)?);
        // }
        output.push(super::Analyzer::match_symbol(
            tokens
                .next()
                .ok_or_else(|| CompilerError::Statement("miss }".into()))?,
            SymbolType::CloseBrace,
        )?);
        if let Some(Token::Keyword(KeywordType::Else)) = tokens.peek() {
            output.push(KeywordType::Else.to_xml());
            // skip [
            let _ = tokens.next();
            // {
            output.push(super::Analyzer::match_symbol(
                tokens
                    .next()
                    .ok_or_else(|| CompilerError::Statement("miss {".into()))?,
                SymbolType::OpenBrace,
            )?);
            // statements
            output.push(Self::compile_statements(tokens)?);
            // }
            output.push(super::Analyzer::match_symbol(
                tokens
                    .next()
                    .ok_or_else(|| CompilerError::Statement("miss }".into()))?,
                SymbolType::CloseBrace,
            )?);
        }
        output.push("</ifStatement>".into());
        Ok(output.join("\n"))
    }
    /// The compiler flow unit of let statement
    fn compile_let_statement(tokens: &mut Peekable<Iter<'_, Token>>) -> CompilerResult<String> {
        let mut output = vec!["<letStatement>".to_string()];
        // let
        output.push(super::Analyzer::match_keyword(
            tokens
                .next()
                .ok_or_else(|| CompilerError::Statement("must start with let".into()))?,
            KeywordType::Let,
        )?);
        // varname
        output.push(
            super::expression::ExpressionCompilerUnit::flow_unit_identifier(
                tokens
                    .next()
                    .ok_or_else(|| CompilerError::Statement("miss var name".into()))?,
            )?,
        );
        if let Some(Token::Symbol(SymbolType::OpenBracket)) = tokens.peek() {
            output.push(SymbolType::OpenBracket.to_xml());
            // skip [
            let _ = tokens.next();
            output.push(super::expression::ExpressionCompilerUnit::compile_expression(tokens)?);
            output.push(super::Analyzer::match_symbol(
                tokens
                    .next()
                    .ok_or_else(|| CompilerError::SubroutineCall("miss ]".into()))?,
                SymbolType::CloseBracket,
            )?);
        }
        output.push(super::Analyzer::match_symbol(
            tokens
                .next()
                .ok_or_else(|| CompilerError::SubroutineCall("miss =".into()))?,
            SymbolType::Equal,
        )?);
        output.push(super::expression::ExpressionCompilerUnit::compile_expression(tokens)?);
        // ;
        output.push(super::Analyzer::match_symbol(
            tokens
                .next()
                .ok_or_else(|| CompilerError::Statement("miss ;".into()))?,
            SymbolType::Semicolon,
        )?);
        output.push("</letStatement>".into());
        Ok(output.join("\n"))
    }
}
